<?php
/**
 * [ActiveRecord] Mysql接口类
 * 
 * @version $Id: Mysql.php 427 2013-08-02 08:21:18Z linsir123 $
 * @package Mysql
 */
class LpMysql
{
	/**
	 * @var 配置信息
	 */
	public $dsn;
	
	/**
	 * @var 数据库名称
	 */
	public $dbName;
	
	/**
	 * @var 是否开启调试
	 */
	public $debug;
	
	/**
	 * @var 链接句柄
	 */
	public $link;
	
	/**
	 * @var 查询记录集合
	 */
	public $queries = array();
	
	/**
	 * @var 查询次数
	 */
	public $numQuery = 0;
	
	/**
	 * @var [上次]插入自增长ID
	 */
	public $insertId;
	
	/**
	 * @var [上次]操作影响行数
	 */
	public $affectedRows;
	
	/**
	 * @var [上次]错误码
	 */
	public $error;
	
	/**
	 * @var [上次]结果集
	 */
	private $_res;
	
	/**
	 * 构造函数
	 * 
	 * @param int $dsnNumber 连接数据源序号,自动从"main.dsn"中加载
	 */
	public function __construct($dsnNumber = 0)
	{
		$this->debug = App::O('main.debug');
		$this->dsn = App::O('main.dsn.'. $dsnNumber);
		
		///
		$this->_connect();
	}
	
	/**
	 * 析构函数
	 */
	public function __destruct()
	{
		if ($this->link) 
			@mysql_close($this->link);
	}
	
	/**
	 * 用于判断连接是否超时,wait-timeout
	 * // 主要处理"MySQL server has gone away"
	 */
	public function ping()
	{
		if ( ! @mysql_ping($this->link)) {
			@mysql_close($this->link);
			
			///
			$this->_connect();
		}
	}
	
	/**
	 * // => mysql_query
	 * 
	 * @param string $sql 用于数据操作
	 * @return resource
	 */
	public function operate($sql)
	{
		if ($this->debug) 
			$this->_timerStart();
			
		$this->_res = @mysql_query($sql, $this->link);
		
		if ($this->debug) 
			$this->queries[] = array($sql, $this->_timerStop());
			
		///
		if ($this->error = mysql_error($this->link)) {
			$this->_log($sql);
			return false;
		}
		
		///
		$this->numQuery++;
		return $this->_res;
	}
	
	/**
	 * // => mysql_query => 分析
	 * 
	 * @param string $sql 需要查询的SQL
	 * @param bool $affected 是否返回影响行数
	 * @return bool
	 */
	public function query($sql, $affected = false)
	{
		$this->operate($sql);
		$this->insertId = 0;
		$this->affectedRows = 0;
		
		///
		$pattern = "/^\\s*(insert|delete|update|replace) /i";
		if (preg_match($pattern, $sql, $m)) {
			$this->affectedRows = mysql_affected_rows($this->link);
			
			///
			$type = strtolower($m[1]);
			if (in_array($type, array('insert', 'replace'))) {
				$this->insertId = mysql_insert_id($this->link);
				return $affected ? $this->affectedRows : $this->insertId;
			}
			if (in_array($type, array('update', 'delete'))) {
				if ($affected) 
					return $this->affectedRows;
			}
		}
		
		///
		return empty($this->error);
	}
	
	/**
	 * 取得某个值
	 * 
	 * @param string $sql 查询SQL
	 * @param int $x 数据集的列号
	 * @param int $y 数据集的行号
	 * @return string
	 */
	public function getVar($sql, $x = 0, $y = 0)
	{
		if ($this->query($sql)) {
			$i = 0;
			while ($row = @mysql_fetch_row($this->_res)) {
				if ($i++ == $y) 
					return isset($row[$x])?$row[$x]:null;
			}
			
			///
			@mysql_free_result($this->_res);
		}
		return null;
	}
	
	/**
	 * 取得某列字段值
	 * 
	 * @param string $sql 查询SQL
	 * @param int $x 数据集的列号
	 * @return array
	 */
	public function getCol($sql, $x = 0)
	{
		$list = array();
		if ($this->query($sql)) {
			while ($row = @mysql_fetch_row($this->_res)) 
				$list[] = isset($row[$x])?$row[$x]:null;
				
			///
			@mysql_free_result($this->_res);
		}
		return $list;
	}
	
	/**
	 * 取得某行记录
	 * 
	 * @param string $sql 查询SQL
	 * @param int $y 数据集的行号
	 * @return array
	 */
	public function getRow($sql, $y = 0)
	{
		if ($this->query($sql)) {
			$i = 0;
			while ($row = @mysql_fetch_assoc($this->_res)) {
				if ($i++ == $y) 
					return $row;
			}
			
			///
			@mysql_free_result($this->_res);
		}
		return array();
	}
	
	/**
	 * 取得查询后的记录集
	 * 
	 * @param string $sql 查询SQL
	 * @return array
	 */
	public function getResults($sql)
	{
		$list = array();
		if ($this->query($sql)) {
			while ($row = @mysql_fetch_assoc($this->_res)) 
				$list[] = $row;
				
			///
			@mysql_free_result($this->_res);
		}
		return $list;
	}
	
	/**
	 * 取得数据库的数据表列表
	 * 
	 * @return array
	 */
	public function getTables()
	{
		$list = array();
		if ($this->query('SHOW TABLES')) {
			while ($row = @mysql_fetch_row($this->_res)) 
				$list[] = $row[0];
				
			///
			@mysql_free_result($this->_res);
		}
		return $list;
	}
	
	/**
	 * 取得当前的数据库连接
	 * 
	 * @return resource
	 */
	public function getLink()
	{
		return $this->link;
	}
	
	/**
	 * 取得当前查询结果集
	 */
	public function getRes()
	{
		return $this->_res;
	}
	
	/**
	 * 判断某个表是否存在(直接查询时性能更高)
	 * 
	 * @param string $table 待查询的数据表名
	 * @return bool
	 */
	public function existsTable($table)
	{
		$sql = "SELECT 1 FROM `{$table}` LIMIT 0,1";
		$res = $this->operate($sql);
		///
		return ! empty($res);
	}
	
	/**
	 * 取得Mysql版本信息
	 * 
	 * @return string
	 */
	public function version()
	{
		return mysql_get_server_info($this->link);
	}
	
	
	/// 以下为私有方法 ///
	
	
	/**
	 * 连接数据库
	 */
	private function _connect()
	{
		list($host, $user, $pwd, $this->dbName, $charset) = $this->dsn;
		
		///
		if ( ! $this->link = @mysql_connect($host, $user, $pwd, true)) 
			throw new Exception("[Mysql] 无法连接至服务器({$user}@{$host})");
			
		if ( ! @mysql_select_db($this->dbName, $this->link)) 
			throw new Exception("[Mysql] 数据库不存在({$this->dbName})");
			
		///
		if ($this->_supportCollation()) {
			if ( ! isset($charset)) 
				$charset = App::O('main.charset.db');
				
			if ( ! empty($charset)) 
				$this->operate("SET NAMES '{$charset}'");
		}
	}
	
	/**
	 * 记录出错的SQL
	 * 
	 * @param string $sql 出错的SQL
	 */
	private function _log($sql)
	{
		$content = sprintf("[%s] (%s) - %s", 
			mysql_errno($this->link), 
			mysql_error($this->link), 
			$sql);
			
		///
		App::log($content, 'runtime/mysql');
	}
	
	/**
	 * 开启计时器
	 */
	private function _timerStart()
	{
		$this->timeStart = $this->_getMtime();
	}
	
	/**
	 * 返回执行时间
	 * 
	 * @return float
	 */
	private function _timerStop()
	{
		return $this->_getMtime() - $this->timeStart;
	}
	
	/**
	 * 返回`microtime`
	 * 
	 * @return float
	 */
	private function _getMtime()
	{
		list($usec, $sec) = explode(" ", microtime());
		///
		return ((float)$usec + (float)$sec);
	}
	
	/**
	 * 取得Mysql对字符集的支持,是否在4.1.0版本之上
	 * 
	 * @return bool
	 */
	private function _supportCollation()
	{
		return (version_compare(mysql_get_server_info($this->link), '4.1.0', '>='));
	}
}
